{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 我们可以使用$PyTorch$的高级$API$更简洁地实现模型\n",
    "* 在$PyTorch$中,$data$模块提供了数据处理工具，$nn$模块定义了大量的神经网络层和常见损失函数\n",
    "* 我们可以通过“_\"结尾的方法将参数替换，从而初始化参数\n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ['KMP_DUPLICATE_LIB_OK'] = \"TRUE\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.1 生成数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import numpy as np\n",
    "from torch.utils import data\n",
    "from d2l import torch as d2l"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "true_w = torch.tensor([2, -3.4])\n",
    "true_b = 4.2\n",
    "features, labels = d2l.synthetic_data(true_w, true_b, 1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.2 读取数据集"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以调用框架现有地API来读取数据。我们将$features$和$labels$作为API的参数传递，并通过数据迭代器指定$batch\\_size$。此外，布尔值$is\\_train$表示是否希望数据迭代器对象在每个迭代周期内打乱数据。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#@save\n",
    "def load_array(data_arrays, batch_size, is_train = True):\n",
    "    \"\"\"构造一个PyTorch数据迭代器\"\"\"\n",
    "    dataset = data.TensorDataset(*data_arrays)\n",
    "    return data.DataLoader(dataset, batch_size, shuffle=is_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 10\n",
    "data_iter = load_array((features, labels), batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[tensor([[ 2.9040, -0.3212],\n",
       "         [-2.6209,  1.2053],\n",
       "         [ 0.6411, -0.4366],\n",
       "         [-0.4879,  0.7988],\n",
       "         [ 0.9938,  0.7411],\n",
       "         [ 0.1436, -0.6759],\n",
       "         [-0.9848, -1.2383],\n",
       "         [-0.2948,  2.5786],\n",
       "         [-0.2625,  0.0587],\n",
       "         [ 0.5722,  0.0033]]),\n",
       " tensor([[11.1070],\n",
       "         [-5.1371],\n",
       "         [ 6.9754],\n",
       "         [ 0.5092],\n",
       "         [ 3.6653],\n",
       "         [ 6.7801],\n",
       "         [ 6.4348],\n",
       "         [-5.1390],\n",
       "         [ 3.4895],\n",
       "         [ 5.3461]])]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "next(iter(data_iter))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.3 定义模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于标准深度学习模型，我们可以使用框架的预定义好的层。这使我们只需关注使用哪些层来构造模型，而不必关注层的实现细节。我们先定义一个模型变量$net$，它是一个$Sequential$类的实例.$Sequential$类将多个层串联一起。当给定输入数据时，$Sequential$类将数据传递给第一个层，然后将第一层的输出作为第二层的输入。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch import nn\n",
    "\n",
    "net = nn.Sequential(nn.Linear(2, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.4 初始化模型参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "net[0].weight.data.normal_(0, 0.01)\n",
    "net[0].bias.data.fill_(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.5 定义损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss =  nn.MSELoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.6 定义优化算法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当我们实例化一个$SGD$实例时，我们需要指定优化参数以及优化算法所需的超参数字典。小批量随机梯度下降只需要设置$lr$参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer = torch.optim.SGD(net.parameters(), lr=0.03)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.3.7 训练"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 通过$net(X)$生成预测并计算损失$l$（前向传播）\n",
    "* 通过反向传播来计算梯度\n",
    "* 通过调用优化器来更新模型参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 1, loss 0.000108\n",
      "epoch 2, loss 0.000109\n",
      "epoch 3, loss 0.000108\n"
     ]
    }
   ],
   "source": [
    "num_epochs = 3\n",
    "for epoch in range(num_epochs):\n",
    "    for X,y in data_iter:\n",
    "        l = loss(net(X), y)\n",
    "        trainer.zero_grad()\n",
    "        l.backward()\n",
    "        trainer.step()\n",
    "    l = loss(net(features), labels)\n",
    "    print(f'epoch {epoch + 1}, loss {l:f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w的误差: tensor([ 7.9632e-05, -7.4744e-04])\n",
      "b的误差: tensor([-0.0001])\n"
     ]
    }
   ],
   "source": [
    "w = net[0].weight.data\n",
    "print('w的误差:',true_w - w.reshape(true_w.shape))\n",
    "b = net[0].bias.data\n",
    "print('b的误差:',true_b - b)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
